<?php

namespace Tests\Unit;

use Tests\TestCase;
use App\Services\BookmarkService;
use App\Models\Bookmark;
use App\Exceptions\Bookmark\BookmarkValidationErrorException;
use App\Exceptions\Bookmark\UpdateBookmarkErrorException;
use App\Exceptions\Bookmark\DeleteBookmarkErrorException;

class BookmarkServiceTest extends TestCase
{
    protected $service;

    public function setUp()
    {
        parent::setUp();
        $this->service = $this->app->make('App\Services\BookmarkService');
    }

    /**
     * @test
     */
    public function shouldCreateANewBookmark()
    {
        $data = [
            'title' => $this->faker->sentence,
            'description' => $this->faker->paragraph,
            'url' => $this->faker->url,
        ];
        $bookmark = $this->service->create($data);

        $this->assertInstanceOf(Bookmark::class, $bookmark);
        $this->assertEquals($data['title'], $bookmark->title);
        $this->assertEquals($data['description'], $bookmark->description);
        $this->assertEquals($data['url'], $bookmark->url);
    }

    /**
     * @test
     */
    public function shouldFailValidationWhenCreateANewBookmarkWithMissingFields()
    {
        $this->expectException(BookmarkValidationErrorException::class);

        $data = [
            'title' => $this->faker->sentence,
        ];
        $bookmark = $this->service->create($data);
    }

    /**
     * @test
     */
    public function shouldUpdateABookmark()
    {
        $data = [
            'title' => $this->faker->sentence,
            'description' => $this->faker->paragraph,
            'url' => $this->faker->url,
        ];
        $bookmark = factory(Bookmark::class)->create();
        $updated = $this->service->update($data, $bookmark->id);

        $this->assertEquals($data['title'], $updated->title);
        $this->assertEquals($data['description'], $updated->description);
        $this->assertEquals($data['url'], $updated->url);
    }

    /**
     * @test
     */
    public function shouldFailValidationWhenUpdateABookmarkWithEmptyFields()
    {
        $this->expectException(BookmarkValidationErrorException::class);

        $data = [
            'title' => null,
            'description' => null,
            'url' => null,
        ];
        $bookmark = factory(Bookmark::class)->create();
        $this->service->update($data, $bookmark->id);
    }

    /**
     * @test
     */
    public function shouldThrowAnExceptionIfUpdateFails()
    {
        $this->expectException(UpdateBookmarkErrorException::class);

        $data = [
            'title' => $this->faker->sentence,
            'description' => $this->faker->paragraph,
            'url' => $this->faker->url,
        ];
        $this->service->update($data, 1);
    }

    /**
     * @test
     */
    public function shouldReturnAllBookmarks()
    {
        $bookmarks = factory(Bookmark::class, 10)->create();
        $result = $this->service->all();

        $this->assertEquals($bookmarks->toArray(), $result);
    }

    /**
     * @test
     */
    public function shouldReturnABookmark()
    {
        $bookmark = factory(Bookmark::class)->create();
        $result = $this->service->find($bookmark->id);

        $this->assertEquals($bookmark->toArray(), $result->toArray());
    }

    /**
     * @test
     */
    public function shouldDeleteAnExistingBookmark()
    {
        $bookmark = factory(Bookmark::class)->create();
        $result = $this->service->delete($bookmark->id);

        $this->assertTrue($result);
    }

    /**
     * @test
     */
    public function shouldThrowAnExceptionIfDeleteANonExistentBookmark()
    {
        $this->expectException(DeleteBookmarkErrorException::class);

        $result = $this->service->delete(1);
    }

    /**
     * @test
     */
    public function shouldThrowAnExceptionIfValidationFails()
    {
        $this->expectException(BookmarkValidationErrorException::class);

        $rules = [
            'title' => ['required'],
            'description' => ['required'],
        ];
        $data = [
            'title' => 'test'
        ];
        $this->service->validate($data, $rules);
    }

    /**
     * @test
     */
    public function shoudlReturnAStringGivenAnArrayOfMessages()
    {
        $data = [
            [
                'message1' => ['An error occurred.'],
                'message2' => ['Another error occurred.'],
            ],
        ];
        $result = $this->invokeMethod($this->service, 'getValidationMessage', $data);

        $this->assertEquals('An error occurred. Another error occurred.', $result);
    }
}
